package Model;

/**
 * The DecisionMatrix, from which any Being can take a decision in any of the 8
 * neighbours direction
 */
public class DecisionMatrix {
    // ATTRIBUTE

    private int[] decisionN;
    private int[] decisionNE;
    private int[] decisionE;
    private int[] decisionSE;
    private int[] decisionS;
    private int[] decisionSO;
    private int[] decisionO;
    private int[] decisionNO;

    // CONSTRUCTOR
    public DecisionMatrix() {
        decisionN = new int[Parameter.getNumberChoiceDecision()];
        decisionNE = new int[Parameter.getNumberChoiceDecision()];
        decisionE = new int[Parameter.getNumberChoiceDecision()];
        decisionSE = new int[Parameter.getNumberChoiceDecision()];
        decisionS = new int[Parameter.getNumberChoiceDecision()];
        decisionSO = new int[Parameter.getNumberChoiceDecision()];
        decisionO = new int[Parameter.getNumberChoiceDecision()];
        decisionNO = new int[Parameter.getNumberChoiceDecision()];

        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            decisionN[i] = 0;
            decisionNE[i] = 0;
            decisionE[i] = 0;
            decisionSE[i] = 0;
            decisionS[i] = 0;
            decisionSO[i] = 0;
            decisionO[i] = 0;
            decisionNO[i] = 0;

        }
    }

    // OWN FUNCTION
    // GETTER - SETTER
    // i = 0 -> ESCAPE
    // i = 1 -> MOVE POI
    // i = 2 -> DoAction
    // i = 3 -> Go Target
    public int getDecisionN(int i) {
        return decisionN[i];
    }

    public void setDecisionN(int i, int decisionN) {
        this.decisionN[i] = decisionN;
    }

    public int getDecisionNE(int i) {
        return decisionNE[i];
    }

    public void setDecisionNE(int i, int decisionNE) {
        this.decisionNE[i] = decisionNE;
    }

    public int getDecisionE(int i) {
        return decisionE[i];
    }

    public void setDecisionE(int i, int decisionE) {
        this.decisionE[i] = decisionE;
    }

    public int getDecisionSE(int i) {
        return decisionSE[i];
    }

    public void setDecisionSE(int i, int decisionSE) {
        this.decisionSE[i] = decisionSE;
    }

    public int getDecisionS(int i) {
        return decisionS[i];
    }

    public void setDecisionS(int i, int decisionS) {
        this.decisionS[i] = decisionS;
    }

    public int getDecisionSO(int i) {
        return decisionSO[i];
    }

    public void setDecisionSO(int i, int decisionSO) {
        this.decisionSO[i] = decisionSO;
    }

    public int getDecisionO(int i) {
        return decisionO[i];
    }

    public void setDecisionO(int i, int decisionO) {
        this.decisionO[i] = decisionO;
    }

    public int getDecisionNO(int i) {
        return decisionNO[i];
    }

    public void setDecisionNO(int i, int decisionNO) {
        this.decisionNO[i] = decisionNO;
    }

    public int getDecision(DirectDirection directDirection, int i) {
        int res = 0;
        switch (directDirection) {
            case North:
                res = decisionN[i];
                break;
            case NorthEast:
                res = decisionNE[i];
                break;
            case East:
                res = decisionE[i];
                break;
            case SouthEast:
                res = decisionSE[i];
                break;
            case South:
                res = decisionS[i];
                break;
            case SouthWest:
                res = decisionSO[i];
                break;
            case West:
                res = decisionO[i];
                break;
            case NorthWest:
                res = decisionNO[i];
                break;
            default:
                // should never reach
                res = -1;
                break;
        }
        return res;
    }

    public double getSum() {
        int res = 0;
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            res += decisionN[i] + decisionNE[i] + decisionNO[i]
                    + decisionS[i] + decisionSO[i] + decisionSE[i]
                    + decisionO[i] + decisionE[i];
        }
        return res;
    }

    public double getSumExceptRowGoTarget() {
        int res = 0;
        for (int i = 0; i < Parameter.getNumberChoiceDecision() - 1; i++) {
            res += decisionN[i] + decisionNE[i] + decisionNO[i]
                    + decisionS[i] + decisionSO[i] + decisionSE[i]
                    + decisionO[i] + decisionE[i];
        }
        return res;
    }

    public void normalize() {
        double total = getSum();
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            decisionN[i] /= total;
            decisionNE[i] /= total;
            decisionE[i] /= total;
            decisionSE[i] /= total;
            decisionS[i] /= total;
            decisionSO[i] /= total;
            decisionO[i] /= total;
            decisionNO[i] /= total;
        }
    }

    public void sumInStats() {
        decisionN[1] += decisionN[0];
        decisionN[2] += decisionN[1];
        decisionN[3] += decisionN[2];

        decisionNE[0] += decisionN[3];
        decisionNE[1] += decisionNE[0];
        decisionNE[2] += decisionNE[1];
        decisionNE[3] += decisionNE[2];

        decisionE[0] += decisionNE[3];
        decisionE[1] += decisionE[0];
        decisionE[2] += decisionE[1];
        decisionE[3] += decisionE[2];

        decisionSE[0] += decisionE[3];
        decisionSE[1] += decisionSE[0];
        decisionSE[2] += decisionSE[1];
        decisionSE[3] += decisionSE[2];

        decisionS[0] += decisionSE[3];
        decisionS[1] += decisionS[0];
        decisionS[2] += decisionS[1];
        decisionS[3] += decisionS[2];

        decisionSO[0] += decisionS[3];
        decisionSO[1] += decisionSO[0];
        decisionSO[2] += decisionSO[1];
        decisionSO[3] += decisionSO[2];

        decisionO[0] += decisionSO[3];
        decisionO[1] += decisionO[0];
        decisionO[2] += decisionO[1];
        decisionO[3] += decisionO[2];

        decisionNO[0] += decisionO[3];
        decisionNO[1] += decisionNO[0];
        decisionNO[2] += decisionNO[1];
        decisionNO[3] += decisionNO[2];

    }

    public boolean isZero() {
        return (getSum() == 0);
    }

    public Action chooseAction() {
        Action res = null;
        DirectDirection firstDirectDirection = DirectDirection.North;
        DirectDirection secondDirectDirection = DirectDirection.North;

        double randomActionChosen = Math.random();
        randomActionChosen *= decisionNO[3]; // multiply by the suminstats of matrix.
        //actionChosen = (int) actionChosen;
        //System.out.println("randomActionChosen : " + randomActionChosen);

        int i = 0; // action
        int j = 1;
        int n = 0; // number of tests
        boolean keepOn = true;

        if ((0 <= randomActionChosen) && (randomActionChosen <= getDecision(firstDirectDirection, 0))) {
            res = new Action(firstDirectDirection, j);
            keepOn = false;
        }

        while (keepOn) {
            int firstLimit = getDecision(firstDirectDirection, i);
            int secondLimit = getDecision(secondDirectDirection, j);

            //test
            if ((firstLimit <= randomActionChosen) // we are in good Decision
                    && (randomActionChosen <= secondLimit)) {
                res = new Action(secondDirectDirection, j);
                keepOn = false;
            }
            n++;


            // changing i && DirectDirection in order N-NE-E-SE-S-SO-O-NO
            if (j == Parameter.getNumberChoiceDecision() - 1) {
                j = 0;
                switch (secondDirectDirection) {
                    case North:
                        secondDirectDirection = DirectDirection.NorthEast;
                        break;
                    case NorthEast:
                        secondDirectDirection = DirectDirection.East;
                        break;
                    case East:
                        secondDirectDirection = DirectDirection.SouthEast;
                        break;
                    case SouthEast:
                        secondDirectDirection = DirectDirection.South;
                        break;
                    case South:
                        secondDirectDirection = DirectDirection.SouthWest;
                        break;
                    case SouthWest:
                        secondDirectDirection = DirectDirection.West;
                        break;
                    case West:
                        secondDirectDirection = DirectDirection.NorthWest;
                        break;
                    case NorthWest:
                        secondDirectDirection = DirectDirection.North;
                        break;
                    default:
                        // should never reach here.
                        return null;
                }

            } else {
                j++;
            }

            if (i == Parameter.getNumberChoiceDecision() - 1) {
                i = 0;
                switch (firstDirectDirection) {
                    case North:
                        firstDirectDirection = DirectDirection.NorthEast;
                        break;
                    case NorthEast:
                        firstDirectDirection = DirectDirection.East;
                        break;
                    case East:
                        firstDirectDirection = DirectDirection.SouthEast;
                        break;
                    case SouthEast:
                        firstDirectDirection = DirectDirection.South;
                        break;
                    case South:
                        firstDirectDirection = DirectDirection.SouthWest;
                        break;
                    case SouthWest:
                        firstDirectDirection = DirectDirection.West;
                        break;
                    case West:
                        firstDirectDirection = DirectDirection.NorthWest;
                        break;
                    case NorthWest:
                        firstDirectDirection = DirectDirection.North;
                        break;
                    default:
                        // should never reach here.
                        return null;
                }

            } else {
                i++;
            }

            // secure test -- Shoud not reach
            if (n > Parameter.getNumberChoiceDecision() * 8) // 8 DirectDirection
            {
                keepOn = false;
                return null;
            }
        }

        //res = new Action(secondDirectDirection, i);
        return res;
    }

    public void displayInTerminal() {
        System.out.print("North : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionN[i] + " ");
        }
        System.out.println();
        System.out.print("NorthEast : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionNE[i] + " ");
        }
        System.out.println();
        System.out.print("East : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionE[i] + " ");
        }
        System.out.println();
        System.out.print("SouthEast : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionSE[i] + " ");
        }
        System.out.println();
        System.out.print("South : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionS[i] + " ");
        }
        System.out.println();
        System.out.print("SouthWest : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionSO[i] + " ");
        }
        System.out.println();
        System.out.print("West : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionO[i] + " ");

        }
        System.out.println();
        System.out.print("NorthWest : ");
        for (int i = 0; i < Parameter.getNumberChoiceDecision(); i++) {
            System.out.print(this.decisionNO[i] + " ");
        }
    }

    /**
     * ************************
     */
    /* OPERATIONS              */
    /**
     * ************************
     */
    public void addVectorToRow(int row, int[] vector) {
        if (vector.length == 8) {
            decisionNO[row] += vector[0];
            decisionN[row] += vector[1];
            decisionNE[row] += vector[2];
            decisionO[row] += vector[3];
            decisionE[row] += vector[4];
            decisionSO[row] += vector[5];
            decisionS[row] += vector[6];
            decisionSE[row] += vector[7];
        } else {
            System.out.println("Wrong vector size --in addVectorToRow");
        }
    }

    public void multiplyVectorToRow(int row, int[] vector) {
        if (vector.length == 8) {
            decisionNO[row] *= vector[0];
            decisionN[row] *= vector[1];
            decisionNE[row] *= vector[2];
            decisionO[row] *= vector[3];
            decisionE[row] *= vector[4];
            decisionSO[row] *= vector[5];
            decisionS[row] *= vector[6];
            decisionSE[row] *= vector[7];
        } else {
            System.out.println("Wrong vector size --in multiplyVectorToRow");
        }
    }

    void majWeight(DirectDirection direction, int line, int actionWeight) {
        if (direction == null) {
            System.out.println("Erreur, direction nulle -- In Matrix.majWeight");
            return;
        }

        switch (direction) {
            case NorthWest:
                this.setDecisionNO(line, actionWeight);
                break;
            case North:
                this.setDecisionN(line, actionWeight);
                break;
            case NorthEast:
                this.setDecisionNE(line, actionWeight);
                break;
            case West:
                this.setDecisionO(line, actionWeight);
                break;
            case East:
                this.setDecisionE(line, actionWeight);
                break;
            case SouthWest:
                this.setDecisionSO(line, actionWeight);
                break;
            case South:
                this.setDecisionS(line, actionWeight);
                break;
            case SouthEast:
                this.setDecisionSE(line, actionWeight);
                break;
        }
    }

    public boolean isZeroExceptRowGoTarget() {
        return (getSumExceptRowGoTarget() == 0);
    }
}
